#ifndef TUPLE_HELPER_HPP__
#define TUPLE_HELPER_HPP__
#include <tuple>
#include <array>

template<typename T, int N, typename... Rest>
struct makeIdenticalTupleHelper {
  typedef typename makeIdenticalTupleHelper<T, N - 1, T, Rest...>::type type;
};

template<typename T, typename... Rest>
struct makeIdenticalTupleHelper<T, 0, Rest...> {
  typedef std::tuple<Rest...> type;
};

template<typename T, int N> struct makeIdenticalTuple {
  static typename makeIdenticalTupleHelper<T, N>::type make(T t) {
    return typename makeIdenticalTupleHelper<T, N>::type 
      { std::tuple_cat(std::make_tuple(t), makeIdenticalTuple<T, N-1>::make(t)) };
  }
};

template<typename T> struct makeIdenticalTuple<T, 0> {
  static typename makeIdenticalTupleHelper<T, 0>::type 
  make(T t) {
    return typename makeIdenticalTupleHelper<T, 0>::type { };
  }
};


template<typename T, int N, typename... Rest>
struct makeIncrementalTupleHelper {
  typedef typename makeIncrementalTupleHelper<T, N - 1, T, Rest...>::type type;
};

template<typename T, typename... Rest>
struct makeIncrementalTupleHelper<T, 0, Rest...> {
  typedef std::tuple<Rest...> type;
};

template<typename T, int N> struct makeIncrementalTuple {
  static typename makeIncrementalTupleHelper<T, N>::type make(T t) {
    return typename makeIncrementalTupleHelper<T, N>::type 
      { std::tuple_cat(std::make_tuple(t), makeIncrementalTuple<T, N-1>::make(t+1)) };
  }
};

template<typename T> struct makeIncrementalTuple<T, 0> {
  static typename makeIncrementalTupleHelper<T, 0>::type 
  make(T t) {
    return typename makeIncrementalTupleHelper<T, 0>::type { };
  }
};


template<template <int> class T, int N, typename... Rest>
struct makeCompositeTupleHelper {
  typedef typename makeCompositeTupleHelper<T, N-1, T<N>, Rest...>::type type;
};

template<template <int> class T, typename... Rest>
struct makeCompositeTupleHelper<T, 1, Rest...> {
  typedef std::tuple<T<1>, Rest...> type;
};

template<template <int> class T, int N> struct makeCompositeTuple {
  static typename makeCompositeTupleHelper<T, N>::type make() {
    return typename makeCompositeTupleHelper<T, N>::type 
      { std::tuple_cat(makeCompositeTuple<T, N-1>::make(), std::make_tuple(T<N> ()))}; }
  };
  
  template<template <int> class T> struct makeCompositeTuple<T, 1> {
    static typename makeCompositeTupleHelper<T, 1>::type 
    make() {
      return typename makeCompositeTupleHelper<T, 1>::type {};
    }
  };


///////////////////////////////////////////////////////////////////////////////////////
// WATCH OUT, THIS MAY BE BUGGY AND BADLY SO !
template <typename T, int SIZE, int N> struct arrayAsTupleHelper {
  static auto make(const std::array<T, SIZE> & a) -> 
    decltype(std::tuple_cat(std::make_tuple(a[SIZE-N]), 
                       arrayAsTupleHelper<T, SIZE, N-1>::make(a)))
  {
    return std::tuple_cat(std::make_tuple(a[SIZE-N]),
                     arrayAsTupleHelper<T, SIZE, N-1>::make(a));
  }
};

// Template class specialization
template <typename T, int SIZE> struct arrayAsTupleHelper<T, SIZE, 1> {
  static std::tuple<T>  make(const std::array<T, SIZE> &a) {
    return std::make_tuple(std::get<SIZE-1>(a));
  }
};

template <typename T, int SIZE> using arrayAsTuple = arrayAsTupleHelper<T, SIZE, SIZE>;
///////////////////////////////////////////////////////////////////////////////////////




template <typename T1, int SIZE, int DEPTH, typename... T2> 
struct tupleAsArrayHelper {
  static void make(std::array<T1, SIZE> &a, std::tuple<T2...> t) {
    a[SIZE - sizeof...(T2)] = std::get<SIZE - sizeof...(T2)>(t);
    tupleAsArrayHelper<T1, SIZE, DEPTH-1, T2...>::make(a, t);
  }
};

template <typename T1, int SIZE, typename... T2> 
struct tupleAsArrayHelper<T1, SIZE, 0, T2...> {
  static void make(std::array<T1, SIZE> &a, std::tuple<T2...> t) {}
};


template <typename T1, typename... T2> 
struct tupleAsArray {
  static void make(std::array<T1, sizeof...(T2)> &a, std::tuple<T2...> t) {
    tupleAsArrayHelper<T1, sizeof...(T2), sizeof...(T2), T2...>::make(a, t);
  }
};

//////////////////////////////////////////////////////////////////////////////
//////////////////////// FROM SO.com /////////////////////////////////////////
// http://stackoverflow.com/questions/10604794/convert-stdtuple-to-stdarray-c11

template<int... Indices>
struct indices {
  using next = indices<Indices..., sizeof...(Indices)>;
};

template<int Size>
struct buildIndices {
  using type = typename buildIndices<Size - 1>::type::next;
};

template<>
struct buildIndices<0> {
  using type = indices<>;
};

template<typename T>
using Bare = typename std::remove_cv<typename std::remove_reference<T>::type>::type;

template<typename Tuple>
constexpr
typename buildIndices<std::tuple_size<Bare<Tuple>>::value>::type
makeIndices()
{ return {}; }

template<typename Tuple, int... Indices>
std::array<
  typename std::tuple_element<0, Bare<Tuple>>::type,
  std::tuple_size<Bare<Tuple>>::value
  >
  toArray(Tuple&& tuple, indices<Indices...>)
{
  using std::get;
  return {{ get<Indices>(std::forward<Tuple>(tuple))... }};
}

template<typename Tuple>
auto toArray(Tuple&& tuple)
  -> decltype( toArray(std::declval<Tuple>(), makeIndices<Tuple>()) )
{
  return toArray(std::forward<Tuple>(tuple), makeIndices<Tuple>());
}






// template <int... I> struct CCC : VariableTerm<I>... {
//   CCC() : VariableTerm<I>()... {}
// };

// auto conds = makeIdenticalTuple<bool, INTERTASKDIM>::make(false);
// auto  ints = makeIncrementalTuple<int, INTERTASKDIM>::make(7);
// auto  vars = makeCompositeTuple<Variable, INTERTASKDIM>::make();
// auto terms = makeCompositeTuple<VariableTerm, INTERTASKDIM>::make();
// conds: 0
// conds: 0
// conds: 0
// ints : 7
// ints : 8
// ints : 9
// Vars :  VAR (place=1)
// Vars :  VAR (place=2)
// Vars :  VAR (place=3)
// Terms :  TERM : S=0 c=1 v= VAR (place=1) -> factor=1
// Terms :  TERM : S=0 c=1 v= VAR (place=2) -> factor=1
// Terms :  TERM : S=0 c=1 v= VAR (place=3) -> factor=1
#endif // TUPLE_HELPER_HPP__
